package com.example.test.dialogfactory.customerview;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.Adapter;
import com.example.test.dialogfactory.R;
import easier.recycler.BaseViewHolder;
import easier.recycler.MultiTypeAdapter;
import easier.recycler.TypeViewHolder;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.Function;
import io.reactivex.rxjava3.functions.Function3;
import io.reactivex.rxjava3.schedulers.Schedulers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * 下拉刷新和加载更多模型,内存优化,最多只加载三页数据,例如加载更多时请求5页数据,会同时请求4,5,6三页数据展示,这样既获得了最新的数据,又保证刷新时界面没有变化
 *
 * @param <T> 数据类型
 */
public abstract class RecyclerPageBridge< T > {

    //用于显示数据
    protected final RecyclerView mRecyclerView;
    private final MultiTypeAdapter mAdapter;

    //所有显示数据
    protected List< TypeViewHolder > mViewHolders = new ArrayList<>( 0 );
    //当前显示的数据
    protected List< T > mCurrent = new ArrayList<>();
    //保存请求的页面数据,已经排序好,123456
    protected List< Page< T > > pages = new ArrayList<>( 3 );

    /**
     * 订阅请求数据结束
     */
    @SuppressWarnings( "unchecked" )
    private class FinishObserver implements Observer< List< TypeViewHolder > > {

        /* 标记 0:刷新当前页面 1:refresh完成 2:loadmore完成 */
        private final int mode;

        public FinishObserver( int mode ) {

            this.mode = mode;
        }

        @Override
        public void onSubscribe( @io.reactivex.rxjava3.annotations.NonNull Disposable d ) {

        }

        @Override
        public void onNext( List< TypeViewHolder > typeViewHolders ) {

            mRecyclerView.post( new Runnable() {
                @Override
                public void run() {

                    notifyFinish( mRecyclerView, pages, mode, true );
                    setAdapter( typeViewHolders );
                    onResponse();
                }
            } );
        }

        @Override
        public void onError( Throwable e ) {

            List< Page< ? > > pages = ((PageException) e).mPages;
            List< Page< T > > list = new ArrayList<>();
            for ( Page< ? > page : pages ) {
                list.add( (Page< T >) page );
            }
            Throwable result = e;
            for ( Page< T > page : list ) {
                Throwable exception = page.getException();
                if ( exception != null ) {
                    result = exception;
                }
            }
            Throwable inner = result;

            mRecyclerView.post( new Runnable() {
                @Override
                public void run() {

                    notifyFinish( mRecyclerView, list, mode, false );
                    setAdapter( inner );
                    onResponse();
                }
            } );
        }

        @Override
        public void onComplete() {

        }
    }

    public RecyclerPageBridge( RecyclerView recyclerView ) {

        mRecyclerView = recyclerView;
        mAdapter = new MultiTypeAdapter();
        mRecyclerView.setAdapter( mAdapter );

        resetPage();
    }

    /**
     * 初始化页面数据
     */
    private void resetPage() {

        for ( int i = 0; i < 3; i++ ) {
            pages.add( new Page<>() );
        }
    }

    /**
     * 开始前请求数据
     */
    protected void onRequest() {

    }

    /**
     * 结束请求数据
     */
    protected void onResponse() {

    }

    /**
     * 清除所有数据
     */
    public void clear() {

        int size = mViewHolders.size();

        mViewHolders.clear();
        mCurrent.clear();
        pages.clear();

        for ( int i = 0; i < 3; i++ ) {
            pages.add( new Page<>() );
        }

        Adapter< ? > adapter = mRecyclerView.getAdapter();
        if ( adapter != null ) {
            adapter.notifyItemRangeRemoved( 0, size );
        }
    }

    public void refreshAll() {

        clear();
        refreshPageByPosition( 0 );
    }

    /**
     * @param position 刷新该位置数据
     */
    public void refreshPageByPosition( int position ) {

        onRequest();

        int curr = position / getPageSize() + 1;
        if ( curr < 1 ) {
            curr = 1;
        }

        requestCacheData( curr - 1, curr, curr + 1 ).subscribe( new FinishObserver( 0 ) );
    }

    /**
     * 刷新当前页
     */
    public void refreshCurrentPage() {

        onRequest();

        int first = pages.get( 0 ).getPage();
        requestCacheData( first, first + 1, first + 2 ).subscribe( new FinishObserver( 0 ) );
    }

    /**
     * 下拉刷新,请求上一页数据
     */
    public void refresh() {

        onRequest();

        int minPageIndex = pages.get( 0 ).getPage();
        if ( minPageIndex < 1 ) {
            minPageIndex = 1;
        }

        int prev = minPageIndex - 1;//前一页
        int next = minPageIndex + 1;//后一页

        requestCacheData( prev, minPageIndex, next ).subscribe( new FinishObserver( 1 ) );
    }

    /**
     * 加载更多
     */
    public void loadMore() {

        onRequest();

        int maxPageIndex = 1;
        for ( int i = pages.size() - 1; i >= 0; i-- ) {
            Page< T > page = pages.get( i );
            if ( page.getData() != null && page.getData().size() > 0 ) {
                if ( page.getData().size() < getPageSize() ) {
                    continue;
                }
                maxPageIndex = page.getPage();
                break;
            }
        }

        int prev = maxPageIndex - 1;//前一页
        int next = maxPageIndex + 1;//后一页

        requestCacheData( prev, maxPageIndex, next ).subscribe( new FinishObserver( 2 ) );
    }

    /**
     * 根据页数创建
     *
     * @param page 页数
     *
     * @return 获取数据
     */
    protected Observable< Page< T > > create( int page ) {

        if ( page < 1 ) {
            return Observable.just( new Page<>( page, getPageSize(), new ArrayList<>() ) );
        } else {
            return requestPageData( mRecyclerView, page, getPageSize() )
                .map( new Function< List< T >, Page< T > >() {
                    @Override
                    public Page< T > apply( List< T > ts ) throws Throwable {

                        return new Page<>( page, getPageSize(), ts );//获取的数据
                    }
                } )
                .onErrorReturn( new Function< Throwable, Page< T > >() {
                    @Override
                    public Page< T > apply( Throwable throwable ) throws Throwable {

                        return new Page<>( page, getPageSize(), throwable );
                    }
                } );
        }
    }

    /**
     * 同时请求三页数据,并处理
     *
     * @param prevPage    前一页
     * @param currentPage 当前页
     * @param nextPage    后一页
     *
     * @return 转换为显示数据, 需要自己保证observeOn(AndroidSchedulers.mainThread ())
     */
    protected Observable< List< TypeViewHolder > > requestCacheData( int prevPage, int currentPage, int nextPage ) {

        Observable< Page< T > > prevOb = create( prevPage );
        Observable< Page< T > > current = create( currentPage );
        Observable< Page< T > > nextOb = create( nextPage );

        return Observable
            .zip( prevOb, current, nextOb,
                new Function3< Page< T >, Page< T >, Page< T >, List< TypeViewHolder > >() {
                    @Override
                    public List< TypeViewHolder > apply( Page< T > page1, Page< T > page2, Page< T > page3 ) throws Throwable {

                        if ( page1.getException() != null || page2.getException() != null || page3.getException() != null ) {

                            List< Page< ? > > list = new ArrayList<>();
                            list.add( page1 );
                            list.add( page2 );
                            list.add( page3 );
                            /* 排序 */
                            Collections.sort( list, new Comparator< Page< ? > >() {
                                @Override
                                public int compare( Page< ? > o1, Page< ? > o2 ) {

                                    return o1.getPage() - o2.getPage();
                                }
                            } );

                            throw new PageException( list );
                        } else {
                            return convertPageDataToUIData( page1, page2, page3 );
                        }
                    }
                } )
            .subscribeOn( Schedulers.io() );
    }

    private static class PageException extends Throwable {

        private final List< Page< ? > > mPages;

        public PageException( List< Page< ? > > pages ) {

            mPages = pages;
        }
    }

    /**
     * @return 将分页数据转换为显示数据
     */
    protected List< TypeViewHolder > convertPageDataToUIData(
        Page< T > page1,
        Page< T > page2,
        Page< T > page3 ) {

        if ( (page1.getData().size() == 0 && page2.getData().size() == 0 && page3.getData().size() == 0)/* 数量为0 */
            && (page1.getPage() != 1 && page2.getPage() != 1 && page3.getPage() != 1) /* 不是第一页 */
            && configAuthRefreshAll() /* 启用了自动刷新 */
        ) {
            /* 如果请求的三页数据为空,那么请求首页数据 */
            /* 旧的列表变为首页列表,使用refreshAll旧的列表变为空列表变为首页列表 */
            refreshPageByPosition( 0 );
            return mViewHolders;
        }

        pages.clear();
        pages.add( page1 );
        pages.add( page2 );
        pages.add( page3 );

        /* 排序 */
        Collections.sort( pages, new Comparator< Page< T > >() {
            @Override
            public int compare( Page< T > o1, Page< T > o2 ) {

                return o1.getPage() - o2.getPage();
            }
        } );

        mCurrent.clear();

        for ( int i = 0, pairsSize = pages.size(); i < pairsSize; i++ ) {
            Page< T > pair = pages.get( i );
            mCurrent.addAll( pair.getData() );
        }

        /* 创建界面数据 */
        convert( mCurrent );
        return mViewHolders;
    }

    /**
     * @param typeViewHolders 根据数据显示界面,没有数据显示空界面,有数据更新界面
     */
    private void setAdapter( List< TypeViewHolder > typeViewHolders ) {

        if ( typeViewHolders.size() > 0 ) {

            mAdapter.updateViewHolders( typeViewHolders );

        } else {

            /*空界面*/
            List< TypeViewHolder > list = new ArrayList<>();
            list.add( configEmptyView( mRecyclerView ) );
            mAdapter.updateViewHolders( list );
        }
    }

    private void setAdapter( Throwable e ) {

        TypeViewHolder e1 = configExceptionView( e );
        if ( e1 != null ) {

            List< TypeViewHolder > list = new ArrayList<>();
            list.add( e1 );
            mAdapter.updateViewHolders( list );
        }
    }

    /**
     * 创建所有需要显示的数据,将原始数据转换为显示数据
     *
     * @param continuousData 原始数据
     */
    protected void convert( List< T > continuousData ) {

        mViewHolders.clear();
        int size = continuousData.size();
        for ( int i = 0; i < size; i++ ) {
            mViewHolders.add( buildViewHolder( mRecyclerView, continuousData.get( i ), i ) );
        }
    }

    /**
     * 重新创建界面数据
     */
    public void reCreatePage( OnReCreatePage< T > onReCreatePage ) {

        for ( Page< T > page : pages ) {
            if ( page.getException() != null ) {
                return;
            }
        }

        boolean changed = false;
        for ( Page< T > page : pages ) {
            if ( onReCreatePage != null ) {
                if ( onReCreatePage.onReCreate( page.getPage(), page.getData() ) ) {
                    changed = true;
                }
            }
        }

        if ( changed ) {
            List< TypeViewHolder > list = convertPageDataToUIData( pages.get( 0 ), pages.get( 1 ), pages.get( 2 ) );
            setAdapter( list );
        }
    }

    /**
     * 重新处理页面数据,之后重新生成界面{@link #reCreatePage(OnReCreatePage)}
     *
     * @param <T> 类型
     */
    public interface OnReCreatePage< T > {

        /**
         * @param page     分页
         * @param pageData 分页数据
         *
         * @return true:数据发生变化了
         */
        boolean onReCreate( int page, List< T > pageData );
    }

    /**
     * 请求指定页数据
     *
     * @param recyclerView recycler
     * @param page         请求页
     *
     * @return 用于订阅数据更新
     */
    protected abstract Observable< List< T > > requestPageData( RecyclerView recyclerView, int page, int pageSize );

    /**
     * 为{@link #mRecyclerView}创建指定位置需要的显示数据
     *
     * @param recyclerView 创建recycler每一个条目的显示数据
     * @param itemData     指定位置的原始数据
     * @param position     指定位置
     *
     * @return 显示数据
     */
    protected abstract TypeViewHolder buildViewHolder( RecyclerView recyclerView, T itemData, int position );

    /**
     * 通知刷新数据完成,先通知完成,之后再刷新界面
     *
     * @param recyclerView recycler
     * @param page         页面数据
     * @param mode         0:刷新当前页面 1:refresh完成 2:loadmore完成
     * @param success      :true 请求成功
     */
    protected void notifyFinish( RecyclerView recyclerView, List< Page< T > > page, int mode, boolean success ) {}

    /**
     * @return 配置单页数据数量
     */
    protected int configPageSize() {

        return 15;
    }

    /**
     * @return 获取页面每一页数据量
     */
    public int getPageSize() {

        return configPageSize();
    }

    public List< T > getAll() {

        return mCurrent;
    }

    /**
     * 配置recycler空界面
     */
    @NonNull
    protected TypeViewHolder configEmptyView( RecyclerView recyclerView ) {

        return new BaseViewHolder( R.layout.empty_view );
    }

    @Nullable
    protected TypeViewHolder configExceptionView( Throwable e ) {

        return null;
    }

    /**
     * 当请求的页面数据数量为0时,是否自动刷新数据:例如请求4,5,6页数据,获取到数据数量为0,如果启用自送刷新,那么将重新请求1页数据
     * <p>
     * {@link #convertPageDataToUIData(Page, Page, Page)}
     *
     * @return true:启用自动刷新
     */
    protected boolean configAuthRefreshAll() {

        return true;
    }

    /**
     * 页面数据
     *
     * @param <T> 数据类型
     */
    public static class Page< T > {

        private int page;
        private int pageSize;
        private List< T > data = new ArrayList<>();
        private Throwable exception;

        public Page() {

        }

        public Page( int page, int pageSize, List< T > data ) {

            this.page = page;
            this.pageSize = pageSize;
            this.data = data;
        }

        public Page( int page, int pageSize, Throwable exception ) {

            this.page = page;
            this.pageSize = pageSize;
            this.exception = exception;
        }

        public int getPage() {

            return page;
        }

        public int getPageSize() {

            return pageSize;
        }

        public List< T > getData() {

            return data;
        }

        public Throwable getException() {

            return exception;
        }
    }
}
